package profiles;

import gestures.Gesture;
import gestures.dragGestures.DragGesture;
import gestures.dragGestures.DragGestureEnd;
import gestures.dragGestures.DragGestureStart;
import gestures.dragGestures.DragGestureChange;
import gestures.slideGestures.SlideGesture;
import gestures.slideGestures.SlideGestureEnd;
import gestures.slideGestures.SlideGestureStart;
import gestures.slideGestures.SlideGestureChange;
import gestures.tapGestures.DoubleTapGesture;
import gestures.tapGestures.TapGesture;

import java.awt.geom.Rectangle2D;
import java.beans.EventHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;


import services.GestureClient;
import services.ListenerRegistration;
import services.subscribers.GestureEventArgs;
import services.subscribers.IGestureListener;
import services.subscribers.dragGestures.IDraggingGestureListener;
import services.subscribers.slideGestures.ISlidingGestureListener;

/**
 * Base Application profile class which registers a generic IGestureListener object. This Generic listener translates inbound Gesture events
 * To method calls on the ApplicationProfile interface. The Gestures are translated to method calls via the following convention:
 * 
 * gestureVerb + GestureDelegate_On + gestureType
 * 
 * where gesture verb is like "drag", "tap", "slide", etc.
 * where gesture type is like "DragStart", "SlideChange", "TapEnd",
 * 
 * 
 * @author CLOUTON
 *
 */
public abstract class BaseApplicationProfile implements ApplicationProfile {

	//The capture bounds to uuse for the generic gesture listener
	protected static final Rectangle2D.Float captureBounds = new Rectangle2D.Float(0,0,1,1);
	
	//The gesture client we connect to
	protected GestureClient gestureClient;
	protected ListenerRegistration activeListenerRegistration;
	
	//A map of gesture classes to method objects
	private Map<Class<? extends Gesture>, Method> gestureToDelegateMethod;
	
	/**
	 * Constructor which takes the GestureClient we should register to listen to events from
	 * 
	 * @param gl
	 */
	public BaseApplicationProfile(GestureClient gl) {
		//Setup the method map
		gestureToDelegateMethod = new HashMap<Class<? extends Gesture>, Method>();
		
		//Sets the listener
		setListener(gl);		
	}

	/**
	 * Sets the gester listener.
	 * 
	 */
	@Override
	public void setListener(GestureClient gl) {
		//Remove any previous listener registration if it exists
		if( this.gestureClient != null && activeListenerRegistration != null ) {
			this.gestureClient.unRegisterListener(activeListenerRegistration);
		}
		
		//Store a reference to this profile objec to use a method proxy object below
		final ApplicationProfile profileDelegate = this;
		
		this.gestureClient = gl;
		
		//Make a new listener registration that is generic and pass an anonymous IGestureListener
		activeListenerRegistration = new ListenerRegistration("baseProfileGeneric", captureBounds, new IGestureListener() {
			
			@Override
			public void actionPerformed(Object sender, GestureEventArgs eventArgs) {
				
				//Attempt to invoke the delegate method
				try {
					//Get the class of the Gesture event
					Class<? extends Gesture> gestureClass = eventArgs.getGesture().getClass();
					
					//Find the delegate method for this gesture object
					Method delgateMethod = null;
					
					//If the method is already in the map, no need to get a Method object
					if( gestureToDelegateMethod.containsKey(gestureClass ) ) {
						delgateMethod = gestureToDelegateMethod.get(gestureClass);
					}
					else {
						String delegateMethodNameForGesture = getDelegateMethodNameForGesture(gestureClass);
						//System.out.println("Mapping gesture class:" + gestureClass + " to method name:" + delegateMethodNameForGesture );
						gestureToDelegateMethod.put(gestureClass,
													delgateMethod = ApplicationProfile.class.getMethod(
														delegateMethodNameForGesture,
														Object.class, GestureEventArgs.class)
													);
					}
					
					//Call on the parent application profile object
					delgateMethod.invoke(profileDelegate, sender, eventArgs);
					
				} catch (IllegalArgumentException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (IllegalAccessException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (InvocationTargetException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (SecurityException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					//There may be unknown Gesture events that occur that we want to fail gracefully on, so no need to show stack trace
					// TODO Auto-generated catch block
					//e.printStackTrace();
				}
				
			}
		},
		//Make it a generic listener (recieves all gestures)
		true
		);
		gl.registerListener(
				activeListenerRegistration
		);
	}

	/**
	 * Utility method for generating the method name. Method name is translated as follows:
	 * 
	 * gestureVerb + GestureDelegate_On + gestureType
	 * 
	 * where gesture verb is like "drag", "tap", "slide", etc.
	 * where gesture type is like "DragStart", "SlideChange", "TapEnd",
	 * @param gestureClass
	 * @return
	 */
	protected String getDelegateMethodNameForGesture(
			Class<? extends Gesture> gestureClass) {
		String gestureName = gestureClass.getSimpleName();
		String gestureVerb = gestureName.substring(0, gestureName.indexOf("Gesture"));
		String gestureType = gestureName.replaceAll("Gesture", "");
		
		String methodName = StringUtils.uncapitalize(gestureVerb) + "GestureDelegate_On" + gestureType ;
		return methodName;
	}

	/** Skeleton delegate methods **/
	
	@Override
	public void tapGestureDelegate_OnTap(Object source, GestureEventArgs e) {
		// TODO Auto-generated method stub
	}

	@Override
	public void tapGestureDelegate_OnDoubleTap(Object source, GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void tapGestureDelegate_OnHold(Object source, GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void slideGestureDelegate_OnSlideStart(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void slideGestureDelegate_OnSlideChange(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void slideGestureDelegate_OnSlideEnd(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void dragGestureDelegate_OnDragStart(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void dragGestureDelegate_OnDragChange(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
	}

	@Override
	public void dragGestureDelegate_OnDragEnd(Object source, GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void zoomGestureDelegate_OnZoomStart(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void zoomGestureDelegate_OnZoomChange(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void zoomGestureDelegate_OnZoomEnd(Object source, GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void holdSlideGestureDelegate_OnHoldSlideStart(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void holdSlideGestureDelegate_OnHoldSlideChange(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void holdSlideGestureDelegate_OnHoldSlideEnd(Object source,
			GestureEventArgs e) {
		// TODO Auto-generated method stub
		
	}
	
}
